#include "cmdFunc.h"
#include "54func.h"
#include "log.h"


// 接收确认信息, 信息正确时返回1, 错误时返回0
int recvMsg(int sockfd, train_t* ptrain) {
    char msgFlag[1] = {0};
    recvn(sockfd, msgFlag, sizeof(msgFlag));
    if(msgFlag[0] == 'e') {
        memset(ptrain, 0, sizeof(train_t));
        recvn(sockfd, &ptrain->length, sizeof(int));
        recvn(sockfd, ptrain->data, ptrain->length);
        printf("%s\n", ptrain->data);
        return 0;
    }
    return 1;
}

// 用小火车发送字符串
int strSend(int netfd, train_t* ptrain, const char* str) {
    memset(ptrain, 0, sizeof(train_t));
    ptrain->length = strlen(str) + 1;
    memcpy(ptrain->data, str, ptrain->length);
    send(netfd, ptrain, ptrain->length + sizeof(int), MSG_NOSIGNAL);
    return 0;
}

// 发送成功信息
int successSend(int netfd) {
    send(netfd, "o", 1, MSG_NOSIGNAL);
    return 0;
}

// 发送错误信息, 如果填入NULL, 发送一个'e'报错信号
// 如果填入字符串, 将其作为报错信息发送给客户端
int errorSend(int netfd, const char* errorMsg) {
    if(errorMsg == NULL) {
        send(netfd, "e", 1, MSG_NOSIGNAL);
    }
    else {
        train_t train;
        strSend(netfd, &train, errorMsg);
    }
    return 0;
}

// pass
// cwd栈出栈
static int pop(cwd_t *pcwd) {
    if(pcwd->size == 0) {
        return -1;
    }
    pathToken_t* temp = pcwd->top;
    if(temp->pnext != NULL) {
        temp->pnext->pfront = NULL;
    }
    pcwd->top = temp->pnext;
    free(temp);
    pcwd->size--;
    if(pcwd->size == 0) {
        pcwd->bottom = NULL;
    }
    return 0;
}

// cwd栈弹栈
static int push(cwd_t *pcwd,const char *data) {
    pathToken_t* pathToken = (pathToken_t*) calloc(1, sizeof(pathToken_t));
    strcpy(pathToken->data, data);

    if(pcwd->top == NULL) {
        pcwd->top = pathToken;
        pcwd->bottom = pathToken;
    }
    else {
        pathToken->pnext = pcwd->top;
        pcwd->top->pfront = pathToken;
        pcwd->top = pathToken;
    }
    pcwd->size++;
    return 0;
}

// 清除所有目录栈
static int clear(cwd_t *pcwd) {
    pathToken_t* pathToken = pcwd->top;
    while(pathToken != NULL) {
        pathToken_t* temp = pathToken;
        pathToken = pathToken->pnext;
        free(temp);
    }
    pcwd->top = NULL;
    pcwd->bottom = NULL;
    pcwd->size = 0;
    return 0;
}

// pass
// 处理当前的token，对cwd进行相应的出栈入栈
static int cwdCtl(cwd_t *pcwd, char* token) {
    if(strcmp(token, "..") == 0) {
        pop(pcwd);
    }
    else if(strcmp(token, ".") != 0){
        push(pcwd, token);
    }

    return 0;
}

// pass
// 通过传入的虚拟地址，修改cwd栈的结构
static int parsePath(cwd_t *pcwd, char* virtualPath) {
    if(virtualPath[0] == '/') {
        clear(pcwd);
    }
    char* token;
    const char delim[] = "/";
    token = strtok(virtualPath, delim);
    if(token != NULL) {
        cwdCtl(pcwd, token);
    }
    else {
        return 0;
    }
    while((token = strtok(NULL, delim)) != NULL) {
        cwdCtl(pcwd, token);
    }
    return 0;
}

// pass
int cwdCpy(cwd_t* pcwdSrc, cwd_t* pcwdCpy) {
    pathToken_t* token = pcwdSrc->bottom;
    while(token != NULL) {
        push(pcwdCpy, token->data);
        token = token->pfront;
    }
    return 0;
}

// pass
int destoryCwd(cwd_t* pcwd) {
    pathToken_t* token = pcwd->bottom;
    while(token != NULL && token->pfront != NULL) {
        pathToken_t* temp = token;
        token = token->pfront;
        free(temp);
    }
    free(pcwd);
    return 0;
}

// pass
int realPath(cwd_t* pcwd, char* argv, char* mergePath) {
    cwd_t* cwd = (cwd_t*)calloc(1, sizeof(cwd_t));
    cwdCpy(pcwd, cwd);
    parsePath(cwd, argv);
    getPath(cwd, mergePath);
    destoryCwd(cwd);
    return 0;
}

// cwd栈初始化
int cwdInit(cwd_t **ppcwd) {
    cwd_t *pcwd = (cwd_t*)calloc(1, sizeof(cwd_t));
    *ppcwd = pcwd;
    return 0;
} 

// 传入cmd_t，解析并执行相应的指令函数,传入cwd_t为指令函数传参
int cmdServerHandler(MYSQL* conn, int netfd, cwd_t *pcwd, cmd_t *pcmd) {
    char msg[4096] = {0};
    if(pcmd->argc == 1){
        sprintf(msg,"%s:%s %s",pcmd->user,pcmd->cmdToken,pcmd->argv1);
    }
    else if(pcmd->argc == 2) {
        sprintf(msg,"%s:%s %s %s",pcmd->user,pcmd->cmdToken,pcmd->argv1,pcmd->argv2);
    }
    else {
        sprintf(msg,"%s:%s",pcmd->user,pcmd->cmdToken);
    }
    CLIENT_LOG(msg);
    
    switch(pcmd->cmdToken[0]) {
    case'c': cdCmd(conn, netfd, pcwd, pcmd); break; 
    case'l':
             switch(pcmd->cmdToken[1]) {
             case's':lsCmd(conn, netfd, pcwd, pcmd); break;
             case'o':
                     switch(pcmd->cmdToken[3]){
                     case'i':userLogin(conn,netfd,pcmd);break;
                     case'o':userLogout(pcwd);break;
                     }break;
             }
             break;
    case'p':
             switch(pcmd->cmdToken[1]) {
             case'u':serverGets(conn, netfd, pcwd, pcmd); break;
             case'w':pwdCmd(netfd, pcwd); break;
             }
             break;
    case'u':
             switch(pcmd->cmdToken[4]){
             case'a':userAdd(conn,netfd, pcwd, pcmd);break;
             case'd':userDel(conn,netfd,pcmd);break;
             }
             break;
    case'g': serverPuts(conn, netfd, pcwd, pcmd); break;
    case'm': mkdirCmd(conn, netfd, pcwd, pcmd); break;
    case'r': rmCmd(conn, netfd, pcwd, pcmd); break;
    case'q': userLogout(pcwd);
    }
    return 0;
}

// pass
// 判断传入路径是否是传入用户所有的合法路径
// 返回值: 1(路径正确), 0(路径不正确) 
int pathJudge(MYSQL* conn, const char* realPath, const char* user, const char* type) {
    char sql[4096] = {0};
    bool isValid = false;
    sprintf(sql, "select * from filesystem where "
            "path = '%s' and user = '%s' and type = '%s' and tomb = 0;",
            realPath, user, type);
    mysql_query(conn, sql);
    SQL_LOG(conn);
    MYSQL_RES* res;
    res = mysql_store_result(conn);
    int ret = 0;
    if((ret = mysql_num_rows(res)) != 0) {
        isValid = true;
    }
    mysql_free_result(res);
    if(isValid) {
        return 1;
    }
    return 0;
}

// pass
// 取出cwd栈中所存的路径, pwdstr用于存储路径
int getPath(const cwd_t *pcwd, char *pwdstr) {
    pathToken_t* pathToken = pcwd->bottom;
    strcat(pwdstr, "/");
    while(pathToken != NULL) {
        strcat(pwdstr, pathToken->data);
        pathToken = pathToken->pfront;
        strcat(pwdstr, "/");
    }
    if(strlen(pwdstr) != 1) {
        pwdstr[strlen(pwdstr) - 1] = '\0';
    }
    return 0;
}

// pwd指令(遍历栈),pwdstr用于存储遍历后的结果
int pwdCmd(int netfd, const cwd_t *pcwd) {
    train_t train;
    char pwdstr[PATH_MAX] = {0};
    getPath(pcwd, pwdstr);
    strSend(netfd, &train, pwdstr);    
    return 0;
}

// cd指令,修改cwd栈,cmd_t获取指令及参数
int cdCmd(MYSQL* conn, int netfd, cwd_t *pcwd, cmd_t *pcmd) {
    char mergePath[1024] = {0};
    if(pcmd->argc == 0) {
        printf("cd to root dir.\n");
        clear(pcwd);
        successSend(netfd);
    }
    else if(pcmd->argc == 1) {
        if(pcmd->argv1[0] == '/') {
            clear(pcwd);
            strcpy(mergePath, pcmd->argv1);
        }
        else {
            realPath(pcwd, pcmd->argv1, mergePath);
        }
        if(pathJudge(conn, mergePath, pcmd->user, "d")) {
            printf("cd path exist.\n");
            parsePath(pcwd, mergePath);
            successSend(netfd);
        }
        else {
            printf("cd error.\n");
            errorSend(netfd, NULL);
            errorSend(netfd, "cd 执行失败,路径无效!");
        }
    }
    return 0;
}

// 将整个目录流中的内容发送给客户端
int sendDir(MYSQL* conn, int netfd, const char* path, char* user) {
    char sql[1024] = {0};
    char id[10] = {0};
    MYSQL_ROW row;
    train_t train;
    sprintf(sql, "select id from filesystem where "
            "path = '%s' and user = '%s' and tomb = 0;", path, user);

    mysql_query(conn, sql);

    MYSQL_RES* res = mysql_store_result(conn);
    if(mysql_num_rows(res) == 0);
    else {
        row = mysql_fetch_row(res);
        memcpy(id, row[0], sizeof(id));
        mysql_free_result(res);
        memset(sql, 0, sizeof(sql));
        sprintf(sql, "select filename from filesystem where "
                "preid = %s and tomb = 0;", id);
        mysql_query(conn, sql);
        res = mysql_store_result(conn);
        while((row = mysql_fetch_row(res)) != NULL) {
            strSend(netfd, &train, row[0]);
        }
    }
    memset(&train, 0, sizeof(train_t));
    send(netfd, &train, sizeof(int) + train.length, MSG_NOSIGNAL);
    return 0;
}

// ls指令,cwd_t获取cwd,cmd_t获取指令及参数
int lsCmd(MYSQL* conn, int netfd, cwd_t *pcwd, cmd_t *pcmd) {
    char mergePath[1024] = {0};
    if(pcmd->argc == 0) {
        getPath(pcwd, mergePath);
    }
    else if(pcmd->argc == 1) {
        if(pcmd->argv1[0] == '/') {
            strcpy(mergePath, pcmd->argv1);
        }
        else {
            realPath(pcwd, pcmd->argv1, mergePath);
        }
    }
    if(pathJudge(conn, mergePath, pcmd->user, "d")) {
        successSend(netfd);
        sendDir(conn, netfd, mergePath, pcmd->user);
    }
    else {
        errorSend(netfd, "路径无效!");
    }
    return 0;
}

